/**********************************************************
 * Copyright © 2024 Walkline Wang (https://walkline.wang)
 * Gitee: https://gitee.com/walkline/keypad-for-ops
 **********************************************************/
#ifndef __KEYPAD__
#define __KEYPAD__

#include "Arduino.h"
#include "keycode.h"
#include "src/drivers/74hc165.h"
#include "src/drivers/ch9329.h"
#include "src/drivers/storage.h"
#include "src/effect.h"

constexpr uint8_t HID_KB_MAX = 8;

const uint8_t KEY_MAP[2][IO_COUNT] = {
    {
        /* 键位定义层 */
        4, 2, 3, 11, 10, /* row1 */
        5, 7, 1, 13, 8,  /* row2 */
        6, 14, 0, 12, 9  /* row3 */

        /*
         * 74HC165D 芯片中未使用的数据引脚也需要记录，例如上边的第 7, 14, 15
         * 位，第 15 位未记录是因为第 9 位之后再无实际使用的按键，但是在 配列层
         * 还需要使用 RESERVED 进行占位和补位，使 配列层 数据的数量等于
         * IO_COUNT，实测未补位的情况下会得到无意义的配列键码值。
         */
    },
    {
        /* 配列层1 */
        DELETE, F2, F10, F3, F4,                                  /* row1 */
        LEFT_ALT, RESERVED, ESC, UP_ARROW, ENTER,                 /* row2 */
        LEFT_CTRL, RESERVED, LEFT_ARROW, DOWN_ARROW, RIGHT_ARROW, /* row3 */
        RESERVED                                                  /* hold */
    },
};

class KEYPAD : public _74HC165, public CH9329, public STORAGE {
  public:
    enum keycode_type_e : uint8_t {
        NONE,
        KEYBOARD,
    };

    /* 当前灯光效果 */
    uint8_t light_effect = 0;

    /* 灯光变换速度，毫秒 */
    uint8_t light_speed = 20;

  private:
    enum key_layer_e : uint8_t {
        LAYER_KEY_DEFINITION, /* 键位定义层 */
        LAYER_KEY_LAYER1,     /* 配列层1 */
    };

    /* 记录按键最后一次的状态 */
    uint8_t last_key_status[IO_COUNT / 8] = {0xff};

    /* 当前配列层*/
    uint8_t key_layer = LAYER_KEY_LAYER1;

    int8_t hid_kb_data[HID_KB_MAX] = {0x00};

  public:
    void setup(void);

    /* 获取指定按键对应的键值 */
    uint8_t get_keycode(uint8_t index);

    /* 获取指定按键在 KEY_MAP[0] 中的索引 */
    uint8_t get_layer_index(uint8_t index);

    /* 获取指定按键的触发状态 */
    uint8_t key_status(uint8_t index);

    /*
     * @brief: 判断指定索引位的按键状态是否发生变化
     *
     * @return
     *     - true 按键状态发生变化，status 为发生变化后的状态，并将变化记录在
     * last_key_status[] 数组中
     *     - false 按键状态未发生变化
     */
    bool key_changed(uint8_t index, uint8_t &status);

    /* 通过键值获取按键类型，如：键盘按键、鼠标按键等 */
    uint8_t get_keycode_type(uint8_t keycode);

    /*
     * @brief: 根据按键状态和键值处理 hid_kb_data[] 和 hid_ms_data[] 中的数据
     * @param: status 按键状态
     *          - true 未按下
     *          - false 已按下
     * @param: keycode 按键对应的键值
     * @return: 返回值为 keycode_type_e 中的类型值
     */
    uint8_t process_hid_data(bool status, uint8_t keycode);

    /* 将 hid_kb_data[] 中的数据发送到 CH9329 */
    void send_kb_hid_data(bool kb_changed);

    void switch_effect(void);

  private:
    /* keyboard related */
    void press(uint8_t keycode);

    void unpress(uint8_t keycode);
    /* keyboard related */

    void release_all(void);

    /*
     * @brief: 判断键盘 keycode 是否在 hid_kb_data[] 数组中
     * @return
     *     - true keycode 存在，index 返回 keycode 在 hid_kb_data[]
     * 中的下标
     *     - false keycode 不存在，index 返回第一个 0x00 在 hid_kb_data[]
     * 中的下标，index 等于 HID_MAX 则表示未找到 0x00
     */
    bool keycode_exist(uint8_t keycode, uint8_t &index);

    /* 将 hid_kb_data[] 中的数据发送到 CH9329 */
    void send_kb_data(void);
};

#endif
